
#include <cstdlib>
#include "Gameplay/Play.h"
#include "Engine/Backbone.h"
#include "Gameplay/Piece.h"
#include "GameState/Endgame/Endgame.h"
#include "Engine/Synaptic/Sound.h"

namespace Gameplay {

PiecePreview::PiecePreview(const Engine::Resource::Graphic & targetGraphic, const Misc::Frame & previewFrame, const Misc::Frame & pieceFrame, int pieceID) :
	Visual("gameplay"),
	Reason("gameplay"),
	piece(pieceID),
	previewShadow("previewShadow", "", 2),
	boardGraphic(targetGraphic, 1),
	pieceFrame(pieceFrame),
	previewFrame(previewFrame) {
	char previewShadowName[255];
	sprintf(previewShadowName, "previewShadow%dx%d", pieceFrame.w, pieceFrame.h);
	previewShadow = Engine::Resource::Graphic(previewShadowName, "", 2),
    previewShadow.setPosition(previewFrame.x,previewFrame.y);
    boardGraphic.setPosition(previewFrame.x,previewFrame.y);
    boardGraphic.setFrameSize(previewFrame.w,previewFrame.h);

	/* nobody likes code duplication */
	Sint16 ofx, ofy;
    ofx = piece & 0x000000ff; // column
    ofx *= pieceFrame.w; // from position to pixels
    ofx += pieceFrame.w/2; // center in piece
    ofx -= previewFrame.w/2; // center in board preview
    ofy = (piece & 0x0000ff00) >> 8; // row
    ofy *= pieceFrame.h; // from position to pixels
    ofy += pieceFrame.h/2; // center in piece
    ofy -= previewFrame.h/2; // center in board preview
    Misc::Vector2d v;
    v.x = ofx;
    v.y = ofy;
	boardMovement.setDestination(v);
	boardMovement.setDeparture(v);
    boardMovement.setSpeed(4.0f);
}

PiecePreview::~PiecePreview(){
}

Engine::Resource::GraphicList PiecePreview::display() {
    Engine::Resource::GraphicList gfxList;
    gfxList.push_back(&previewShadow);
    gfxList.push_back(&boardGraphic);
    return gfxList;
}

void PiecePreview::setPiece(int pieceID) {
    piece = pieceID;
    Sint16 ofx, ofy;
    ofx = piece & 0x000000ff; // column
    ofx *= pieceFrame.w; // from position to pixels
    ofx += pieceFrame.w/2; // center in piece
    ofx -= previewFrame.w/2; // center in board preview
    ofy = (piece & 0x0000ff00) >> 8; // row
    ofy *= pieceFrame.h; // from position to pixels
    ofy += pieceFrame.h/2; // center in piece
    ofy -= previewFrame.h/2; // center in board preview
    Misc::Vector2d v;
    v.x = ofx;
    v.y = ofy;
    boardMovement.setDestination(v);
}

// Sets the pieceID for the preview
void PiecePreview::pulse(int delta) {
    // update movement and get position
    Misc::Vector2d v;
    boardMovement.update(delta);
    v = boardMovement.readLocation();
    boardGraphic.setFrameOffset((int)v.x, (int)v.y);
}

Score::Score(const Misc::Frame & scoreFrame, double maxScore) :
	Visual("gameplay"),
	Reason("gameplay"),
	maxScore(maxScore),
	score(0.0f),
	scorebaseGraphic("clock-base", "score", 1),
	pointerGraphic("pointer", "score", 2),
	scoreFrame(scoreFrame),
	flashAnimation(10),
	bonusTimeGfx("bonusTime", "score", 2),
	bonusTimeAnimation(10, 15),
	minusTimeGfx("minusTime", "score", 2),
	minusTimeAnimation(10, 15)
{
    scorebaseGraphic.setPosition(scoreFrame.x, scoreFrame.y);
    scorebaseGraphic.setFrameSize(scoreFrame.w, scoreFrame.h);

    pointerGraphic.setCenter(scoreFrame.x + scoreFrame.w/2, scoreFrame.y + scoreFrame.h/2);
    pointerMovement.setDeparture(Misc::Vector2d(0.0f, 0.0f));
    pointerMovement.setSpeed(2.0f);

	bonusTimeGfx.setFrameSize(200, 25);
	bonusTimeGfx.setFrame(9, 0);
	bonusTimeGfx.setCenter(scoreFrame.x + scoreFrame.w - 20, scoreFrame.y + scoreFrame.h + 10);
	minusTimeGfx.setFrameSize(200, 25);
	minusTimeGfx.setFrame(9, 0);
	minusTimeGfx.setCenter(scoreFrame.x + scoreFrame.w - 20, scoreFrame.y + scoreFrame.h + 10);

	bonusTimeAnimation.setFrame(9);
	minusTimeAnimation.setFrame(9);
    flashAnimation.setWrap();
}

Score::~Score(){
}

Engine::Resource::GraphicList Score::display() {
    Engine::Resource::GraphicList gfxList;
    gfxList.push_back(&pointerGraphic);
	gfxList.push_back(&bonusTimeGfx);
	gfxList.push_back(&minusTimeGfx);
	gfxList.push_back(&scorebaseGraphic);

    return gfxList;
}

void Score::pulse(int delta) {
    pointerMovement.update(delta);
    Misc::Vector2d v = pointerMovement.readLocation();
    pointerGraphic.setRotation(-(v.x/maxScore)*360.0);
    if (score/maxScore <= 0.125) {
        scorebaseGraphic.setFrame(flashAnimation.update(delta), 0);
    }
    else scorebaseGraphic.setFrame(0,0);
	bonusTimeGfx.setFrame(bonusTimeAnimation.update(delta), 0);
	minusTimeGfx.setFrame(minusTimeAnimation.update(delta), 0);
	
	// distance of each clock part
    GameState::Endgame::Endgame::setScore(score, maxScore);

    //scoreupperGraphic.setFrame(0, score/maxScore);
}

double Score::getScore() {
    return score;
}

Play::Play(std::string targetGraphicName, int boardWidth, int boardHeight, int pieceWidth, int pieceHeight) : 
	Sense("gameplay"),
	Reason("gameplay"),
	Visual("gameplay"),
	targetGraphic(targetGraphicName, "board", 0),
	board(boardWidth, boardHeight),
	gfx("bg", "", -2),
	osd("osd-black", "", -1),
	boardPreview(targetGraphic.resize(200.0/(boardWidth*pieceWidth), 100.0/(boardHeight*pieceHeight))),
	boardFrame(75,175, pieceWidth * boardWidth, pieceHeight * boardHeight),
	pieceFrame(75, 175, pieceWidth, pieceHeight),
	nextPiece(board.nextPiece()),
	piecePreview(targetGraphic, Misc::Frame(75,25,125,100), pieceFrame, nextPiece),
	pauseLayer("pauseLayer", "", 2),
	score(Misc::Frame(450,25,125,100), 60.0f),
	pausedGame(false)
{
    targetGraphic.setFrameSize(pieceWidth,pieceHeight);
    targetGraphic.setFrame(0,0);
    targetGraphic.setPosition(75,25);
    targetGraphic.setPadding(62-pieceWidth/2,50-pieceHeight/2);
    //nextPiece = board.nextPiece();
    pieces.push_back(new Piece(nextPiece, pieceFrame, boardFrame, targetGraphic));
    nextPiece = board.nextPiece();

    gfx.setPosition(0,0);
    gfx.setPadding(0,0);
	
	pauseLayer.setPosition(0,0).setPadding(0,0);

    boardPreview.setPosition(225,25); /* ?! TODO: preview should manage itself */

    nextMoveIn = 3000; /* TODO: hardness threshold */

    score.hit(30.0f);
    gameOver = false;
	
	Engine::Synaptic::Sound::loadSound("sounds/wrongPlace.wav");
	Engine::Synaptic::Sound::loadSound("sounds/rightPlace.wav");
	
	GameState::DataStorage<Uint16>::set("gameTime", 0);
}

Play::~Play(){
    //delete pieces;
    while (!pieces.empty()) {
        Piece * p = pieces.front();
        delete p;
        pieces.pop_front();
    }
    
    // updates game time
    GameState::DataStorage<Uint16>::set("lastGameTime", GameState::DataStorage<Uint16>::get("gameTime"));
}

void Play::pulse(int delta) {
    Piece * piece = pieces.back();
    // See if the actual piece fits.
    // Move the piece down or get next piece
    if (pausedGame) return;
	piecePreview.setPiece(nextPiece);
    nextMoveIn -= delta;
    double scoreNow = 0;
    scoreNow = score.miss(delta/2000.0f);
    if (Sense::keyState[SDLK_DOWN]) nextMoveIn -= delta * 2;
    if (Sense::keyState[SDLK_UP]) nextMoveIn += delta / 2;
    if (nextMoveIn <= 0) {
        if (board.canMove(*piece, Misc::DIRECTION_DOWN)) {
            piece->move(Misc::DIRECTION_DOWN);
            nextMoveIn = 500; /* TODO a more challenging timer FIXME magic numbers should go away. */
        } else {
            if (board.fit(*piece)) {
                if (nextPiece == -1) { /* no piece left */
                    gameOver = true;
                    Engine::Backbone::changeContext("endGame");
                    /* congats screen */
                } else {
                    pieces.push_back(new Piece(nextPiece, pieceFrame, boardFrame, targetGraphic));
                    nextPiece = board.nextPiece();
                    piecePreview.setPiece(nextPiece);
                }
				Engine::Synaptic::Sound::play("sounds/rightPlace.wav");
                scoreNow = score.hit(5.0);
				GameState::DataStorage<Uint16>::set("pieceCount", pieces.size());
            } else {
                scoreNow = score.miss(1.0);
                piece->retry();
				Engine::Synaptic::Sound::play("sounds/wrongPlace.wav");
            }
            nextMoveIn = 3000;
        }
    }
    if (scoreNow <= 0.0) {
        gameOver = true;
        Engine::Backbone::changeContext("endGame");
    }
    Uint16 tgameTime = GameState::DataStorage<Uint16>::get("gameTime");
	tgameTime += delta;
    GameState::DataStorage<Uint16>::set("gameTime", tgameTime);
	
}

void Play::keyUp(SDLKey sym, SDLMod mod, Uint16 unicode) {
    Piece * piece = pieces.back();
    switch (sym) {
    case SDLK_LEFT:
        if (pausedGame) break;
        if (board.canMove(*piece, Misc::DIRECTION_LEFT)) {
            piece->move(Misc::DIRECTION_LEFT);
        }
        break;

    case SDLK_RIGHT:
        if (pausedGame) break;
        if (board.canMove(*piece, Misc::DIRECTION_RIGHT)) {
            piece->move(Misc::DIRECTION_RIGHT);
        }
        break;
    default:
        break;
    }
}

Engine::Resource::GraphicList Play::display() {
    Engine::Resource::GraphicList gfxList;
    gfxList.push_back(&boardPreview);
    gfxList.push_back(&osd);
    gfxList.push_back(&gfx);
	if (isPaused()) gfxList.push_back(&pauseLayer);
    return gfxList;
}

bool Play::isOver() {
    return gameOver;
}


} // namespace Gameplay
